package com.tgra;

import com.badlogic.gdx.graphics.GL11;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;

public class First3D_Core implements ApplicationListener {
	Camera cam;
	Maze maze;
	float camRad;
	float sphereRad;

	float walkingdirX;
	float walkingdirZ;

	float distX;     //distance from camera to wherever within cell
	float distZ;

	@Override
	public void create()
	{
		Gdx.gl11.glEnable(GL11.GL_LIGHTING);
		Gdx.gl11.glEnable(GL11.GL_LIGHT0);
		Gdx.gl11.glEnable(GL11.GL_LIGHT1);
		Gdx.gl11.glEnable(GL11.GL_DEPTH_TEST);
		Gdx.gl11.glEnable(GL11.GL_TEXTURE_2D);
		Gdx.gl11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);

		Gdx.gl11.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

		Gdx.gl11.glMatrixMode(GL11.GL_PROJECTION);
		Gdx.gl11.glLoadIdentity();
		Gdx.glu.gluPerspective(Gdx.gl11, 120, 1.333333f, 0.01f, Math.max(1.5f * Maze.MAZE_X, 1.5f * Maze.MAZE_Z));

		Gdx.gl11.glEnableClientState(GL11.GL_VERTEX_ARRAY);

		init();
	}
	
	public void init()
	{
		cam = new Camera(new Point3D(0.5f, 0.4f, 0.5f), new Point3D(0.5f, 0.4f, 2.0f), new Vector3D(0.0f, 1.0f, 0.0f));
		maze = new Maze();

		walkingdirX = -cam.n.x/Math.abs(cam.n.x);
		walkingdirZ = -cam.n.z/Math.abs(cam.n.z);		
		camRad = 0.1f;
		sphereRad = 0.05f;
	}

	private void update() 
	{
		float deltaTime = Gdx.graphics.getDeltaTime();
		boolean forward = false;
		boolean backward = false;

		boolean collX = false;
		boolean collZ = false;

		int cellx = (int) cam.eye.x;
		int cellz = (int) cam.eye.z;

		walkingdirX = -cam.n.x/Math.abs(cam.n.x);
		walkingdirZ = -cam.n.z/Math.abs(cam.n.z);

		distX = distInCell(cellx, cam.eye.x, walkingdirX);
		distZ = distInCell(cellz, cam.eye.z, walkingdirZ);

		if (Gdx.input.isKeyPressed(Input.Keys.UP))
		{
			forward = true;
			cam.slide(0.0f, 0.0f, -1.5f*deltaTime);

			collX = isCollisionX(cellx, cellz);
			collZ = isCollisionZ(cellx, cellz);

			collsionResponseAhead(cellx, cellz, collX, collZ);

		}
		if (Gdx.input.isKeyPressed(Input.Keys.DOWN))
		{
			backward = true;
			walkingdirX *= -1;
			walkingdirZ *= -1;
			distX = 1-distX;
			distZ = 1-distZ;

			cam.slide(0.0f, 0.0f, 1.5f*deltaTime);	

			collX = isCollisionX(cellx, cellz);
			collZ = isCollisionZ(cellx, cellz);

			collsionResponseAhead(cellx, cellz, collX, collZ);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.LEFT))
		{
			if(forward || backward)
			{
				collsionResponseSides(-1.5f * deltaTime, cellx, cellz);
			}
			else
			{
				cam.yaw(-90.0f * deltaTime);				
			}
		}
		if (Gdx.input.isKeyPressed(Input.Keys.RIGHT))
		{
			if(forward || backward)
			{
				collsionResponseSides(1.5f * deltaTime, cellx, cellz);
			}
			else
			{
				cam.yaw(90.0f * deltaTime);				
			}
		}
	}

	private void display()
	{
		Gdx.gl11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

		cam.setModelViewMatrix();

		float[] lightDiffuse = { 1.0f, 1.0f, 1.0f, 1.0f };
		Gdx.gl11.glLightfv(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, lightDiffuse, 0);

		float[] lightPosition = { 5.0f, 10.0f, 15.0f, 1.0f };
		Gdx.gl11.glLightfv(GL11.GL_LIGHT0, GL11.GL_POSITION, lightPosition, 0);

		float[] lightDiffuse1 = { 1.0f, 1.0f, 1.0f, 1.0f };
		Gdx.gl11.glLightfv(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, lightDiffuse1, 0);

		float[] lightPosition1 = { -5.0f, -10.0f, -15.0f, 1.0f };
		Gdx.gl11.glLightfv(GL11.GL_LIGHT1, GL11.GL_POSITION, lightPosition1, 0);

		float[] materialDiffuse = { 1.0f, 1.0f, 1.0f, 1.0f };
		Gdx.gl11.glMaterialfv(GL11.GL_FRONT, GL11.GL_DIFFUSE, materialDiffuse, 0);

		maze.draw();
	}

	@Override
	public void render() {
		update();
		display();
	}

	//Fall skilar fjarlaegd fra myndavel i vegg sem leikmadur stefnir ad
	float distInCell(int cell, float camCoord, float walkingdir)
	{
		if(walkingdir > 0)
		{
			return cell + 1 - camCoord;
		}
		return camCoord - cell;
	}

	//Fall athugar hvort vertical collision hafi att ser stad
	public boolean isCollisionX(int cellx, int cellz)
	{
		if(walkingdirX > 0)
		{
			if(maze.hasEastWall(cellx, cellz))   //check collision with wall
			{
				return  (cellx + 1 - cam.eye.x) < (Maze.THICKNESS/2.0f+camRad);
			}	
		}
		else
		{
			if(maze.hasWestWall(cellx, cellz))
			{
				return cam.eye.x - cellx < (Maze.THICKNESS/2.0f+camRad);
			}
		}

		return false;
	}

	//Fall athugar hvort horizontal collision hafi att ser stad
	public boolean isCollisionZ(int cellx, int cellz)
	{
		if(walkingdirZ > 0)
		{
			if(maze.hasSouthWall(cellx, cellz))
			{
				return (cellz + 1 - cam.eye.z) < (Maze.THICKNESS/2.0f+camRad);
			}
		}		
		else
		{
			if(maze.hasNorthWall(cellx, cellz))
			{
				return cam.eye.z - cellz < (Maze.THICKNESS/2.0f+camRad);
			}
		}
		return false;
	}

	//Fall athugar collision og responde-ar ef eitthvad verdur
	public void collsionResponseAhead(int cellx, int cellz, boolean collX, boolean collZ)
	{
		hasWonGame(cellx,cellz);
		if(collX && collZ)
		{
			distX = Math.round(distX * 100) / 100;    //Round off so player doesn't bounch in a corner
			distZ = Math.round(distZ * 100) / 100;

			if(distX < distZ)  
			{
				cam.eye.x = changeCoord(cellx, walkingdirX);					
			}
			else
			{
				cam.eye.z = changeCoord(cellz, walkingdirZ);
			}
		}
		if(collX)
		{
			cam.eye.x = changeCoord(cellx, walkingdirX);
		}
		else if(collZ)
		{
			cam.eye.z = changeCoord(cellz, walkingdirZ);				
		}
	}

	//Fall faerir myndavel ef collision verdur fra hlidum
	public void collsionResponseSides(float turn, int cellx, int cellz)
	{
		hasWonGame(cellx,cellz);
		cam.slide(turn, 0.0f, 0.0f);	
		if (isCollisionX(cellx, cellz))    //vertical hit
		{
			cam.eye.x = changeCoord(cellx, walkingdirX);
		}
		else if (isCollisionZ(cellx, cellz))    //horizontal hit
		{
			cam.eye.z = changeCoord(cellz, walkingdirZ);
		}
	}

	//Fall faerir myndavel utur vegg ef collision verdur
	float changeCoord(int cellindex, float dir)
	{
		if(dir > 0)
		{
			return (float)(cellindex + 1 - (0.05+camRad));
		}
		else
		{
			return (float)(cellindex + (0.05+camRad));
		}				
	}	
	
	//Fall athugar hvort leikmadur hefur nad kulu og endad leikinn
	public void hasWonGame(int cellx, int cellz)
	{
		if(maze.cells[cellx][cellz].isGoal)
		{
			float dist_sq = ((cellx+0.5f)-cam.eye.x)*((cellx+0.5f)-cam.eye.x) + ((cellz+0.5f)-cam.eye.z)*((cellz+0.5f)-cam.eye.z);
			if (dist_sq < (camRad+sphereRad)*(camRad+sphereRad))
			{
				init();
			}
		}
	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub

	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub

	}

	@Override
	public void resize(int arg0, int arg1) {
		// TODO Auto-generated method stub

	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub

	}

}
